#include <SDL.h>
#include <assert.h>
#include <math.h>
#include "scr_game_data.h"
#include "scr_marine.h"
#include "scr_texture.h"
#include "scr_basic_sprite.h"
#include "scr_game.h"
#include "scr_projectile.h"
#include "scr_math.h"

SCRGameData::SCRGameData()
	: m_pMarine(NULL)
	, m_pEnemy(NULL)
	, m_PowerUpIndex(0)
	, m_pPowerUp(NULL)
{
	ClearBuckets();
	m_ProjectileTexture = SCRGame::GetInstance()->GetResourceManager()->GetTexture("images/projectile.png", true);
	m_PowerUpTexture = SCRGame::GetInstance()->GetResourceManager()->GetTexture("images/powerups.png", false);
	m_pPowerUp = new SCRBasicSprite(m_PowerUpTexture, 32, 32, 0, 0);
	m_BloodTexture = SCRGame::GetInstance()->GetResourceManager()->GetTexture("images/blood.png", true);
	m_Background = SCRGame::GetInstance()->GetResourceManager()->GetTexture("images/background.png", false);
}

SCRGameData::~SCRGameData()
{
	if (m_pPowerUp)
	{
		delete m_pPowerUp;
		m_pPowerUp = NULL;
	}

	if (m_pMarine)
	{
		delete m_pMarine;
		m_pMarine = NULL;
	}

	if (m_pEnemy)
	{
		delete m_pEnemy;
		m_pEnemy = NULL;
	}
}

void SCRGameData::GenerateBlocks(SCRTexturePtr TextureTarget, SCRTexturePtr TextureBlocks)
{
	memset(m_MapBlocksAA, 0, SCR_BLOCK_GRID_COUNT_X * SCR_BLOCK_GRID_COUNT_Y * sizeof(int));
	SDL_Rect SourceRect = { 0, 0, TextureBlocks->GetData()->w, TextureBlocks->GetData()->h };

	for (int BlockIdx = 0; BlockIdx < SCR_BLOCK_COUNT; ++BlockIdx)
	{
		bool Retry = false;
		do
		{
			Retry = false;
			int RandomX = rand() % SCR_BLOCK_GRID_COUNT_X;
			int RandomY = rand() % SCR_BLOCK_GRID_COUNT_Y;
			if (m_MapBlocksAA[RandomX][RandomY])
			{
				Retry = true;
			}
			else
			{
				m_MapBlocksAA[RandomX][RandomY] = -1;
				SDL_Rect DestRect = { TextureBlocks->GetData()->w * RandomX, 
					TextureBlocks->GetData()->h * RandomY, 0, 0 };
				SDL_BlitSurface(TextureBlocks->GetData(), &SourceRect, TextureTarget->GetData(), &DestRect);
			}
		}
		while (Retry);
	}
}

void SCRGameData::ClearCheckMap()
{
	memcpy(m_CheckMapAA, m_MapBlocksAA, SCR_BLOCK_GRID_COUNT_X * SCR_BLOCK_GRID_COUNT_Y * sizeof(int));
}

void SCRGameData::ClearBuckets()
{
	for (int YIdx = 0; YIdx < SCR_MAP_GRID_COUNT_Y; ++YIdx)
	{
		for (int XIdx = 0; XIdx < SCR_MAP_GRID_COUNT_X; ++XIdx)
		{
			m_Buckets[XIdx][YIdx].clear();
		}
	}
}

void SCRGameData::AddToBucket(const int& ObjectIndex, const int& PositionX, const int& PositionY)
{
	int PosX = std::max(0, std::min(SCR_MAP_SIZE_X - 1, PositionX));
	int PosY = std::max(0, std::min(SCR_MAP_SIZE_Y - 1, PositionY));
	m_Buckets[PosX >> 8][PosY >> 8].push_back(ObjectIndex);
}

void SCRGameData::CreateMarine(SCRTexturePtr TextureTop, SCRTexturePtr TextureBottom)
{
	assert(NULL == m_pMarine);

	m_pMarine = new SCRMarine(TextureTop, TextureBottom);
}

void SCRGameData::CreateEnemy(SCRTexturePtr Texture)
{
	assert(NULL == m_pEnemy);

	m_pEnemy = new SCRBasicSprite(Texture, 64, 64, 32, 32);
}

void SCRGameData::InitializeEnemies()
{
	for (int EnemyIdx = 0; EnemyIdx < SCR_ENEMY_COUNT; ++EnemyIdx)
	{
		m_EnemiesA[EnemyIdx].m_Index = EnemyIdx;
	}
}

void SCRGameData::UpdateEnemies(const int& Delta)
{
	for (int EnemyIdx = 0; EnemyIdx < SCR_ENEMY_COUNT; ++EnemyIdx)
	{
		if (0 == m_EnemiesA[EnemyIdx].m_Active)
		{
			int RandomBehavior = rand() & 3;
			if (RandomBehavior == 0)
			{
				m_Blinky.Initialize(&m_EnemiesA[EnemyIdx]);
			}
			else if (RandomBehavior == 1)
			{
				m_Pinky.Initialize(&m_EnemiesA[EnemyIdx]);
			}
			else
			{
				m_Inky.Initialize(&m_EnemiesA[EnemyIdx]);
			}

			break;
		}
	}

	for (int EnemyIdx = 0; EnemyIdx < SCR_ENEMY_COUNT; ++EnemyIdx)
	{
		m_EnemiesA[EnemyIdx].Update(Delta);
		AddToBucket(EnemyIdx, FXFXToInt(m_EnemiesA[EnemyIdx].m_X), FXFXToInt(m_EnemiesA[EnemyIdx].m_Y));
	}
}

void SCRGameData::RenderEnemies(const int& Delta)
{
	for (int EnemyIdx = 0; EnemyIdx < SCR_ENEMY_COUNT; ++EnemyIdx)
	{
		m_EnemiesA[EnemyIdx].Render(Delta);
	}
}

void SCRGameData::UpdateProjectiles(const int& Delta)
{
	for (std::vector<SCRProjectilePtr>::iterator Iterator = m_Projectiles.begin();
		Iterator != m_Projectiles.end();)
	{
		if (!(*Iterator)->IsActive())
		{
			Iterator = m_Projectiles.erase(Iterator);
		}
		else
		{
			++Iterator;
		}
	}

	for (std::vector<SCRProjectilePtr>::iterator Iterator = m_Projectiles.begin();
		Iterator != m_Projectiles.end(); ++Iterator)
	{
		(*Iterator)->Update(Delta);
	}
}

void SCRGameData::RenderProjectiles(const int& Delta)
{
	for (std::vector<SCRProjectilePtr>::iterator Iterator = m_Projectiles.begin();
		Iterator != m_Projectiles.end(); ++Iterator)
	{
		(*Iterator)->Render(Delta);
	}
}

void SCRGameData::FireProjectile(const FX32& Angle, const FX32& Speed, const int& Lifetime, const int& Damage)
{
	SCRProjectilePtr Projectile(new SCRProjectile(GetMarine()->GetFixedX(),
		GetMarine()->GetFixedY(), Angle, Speed, Lifetime, Damage, m_ProjectileTexture));
	m_Projectiles.push_back(Projectile);
}

int SCRGameData::UpdatePowerUps(const int& Delta)
{
	for (int PowerIdx = 0; PowerIdx < SCR_POWERUP_COUNT; ++PowerIdx)
	{
		if (m_PowerUpA[PowerIdx].IsActive())
		{
			SCRRect MarineRect(m_pMarine->GetX() - 16, m_pMarine->GetY() - 16, 32, 32);
			if (m_PowerUpA[PowerIdx].Intersect(MarineRect))
			{
				m_PowerUpA[PowerIdx].m_Active = 0;
				return m_PowerUpA[PowerIdx].GetPower();
			}
		}
	}
	
	return 0;
}

void SCRGameData::RenderPowerUps(const int& Delta)
{
	for (int PowerIdx = 0; PowerIdx < SCR_POWERUP_COUNT; ++PowerIdx)
	{
		if (m_PowerUpA[PowerIdx].IsActive())
		{
			m_pPowerUp->Draw(m_PowerUpA[PowerIdx].m_Rect.m_X, m_PowerUpA[PowerIdx].m_Rect.m_Y, 
				0, m_PowerUpA[PowerIdx].GetPower() - 1);
		}
	}
}
